import java.lang.Thread;
import java.awt.Color;
import java.util.Vector;

/**
 * Animator understands "GRASP animation language" and executes the animation
 * program (also referred to as "animation string") by sending appropriate messages
 * to corresponding GPanel for animation to take effect.  The speed of the Animator
 * can be controlled.
 *
 * Animation Language:
 *
 * Animation Language contains the following commands:
 *   1) V<vertex>    -- highlights the "vertex"
 *   2) E<from>,<to> -- highlights the "edge" form "from" to "to"
 *   3) v<vertex>    -- unhighlights the "vertex" 
 *   4) e<from>,<to> -- unhighlights the "edge" from "from" to "to"
 *   5) R            -- restores all the "vertices" and "edges" to their previous state
 *   6) N            -- when prefixed to any above commands then there will not
 *                      be any waiting for the duration of "DelayPeriod" for this command
 *   7) END          -- indicates the end of animation string. All animation programs
 *                      should have END as the last command
 * (white spaces are not allowed in any command)
 *
 * @version 1.0
 * @author K.Vamsi Krishna
 */
public class Animator extends Thread
{
	// currGPanel is the GPanel object on which this Animator object executes
	// animation commands
	private GPanel currGPanel;
	// currAS is the animation string containing program in GRASP animation language
	private Vector<String> currAS;
	// delayPeriod specifies the delay in milliseconds between execution of each
	// animation command
	private int delayPeriod = 2000;
	// highlighting color
	private Color HLColor;
	// unhightlighting color
	private Color UHLColor;
	/**
	 * Default color for highlighting.
	 */
	public final Color HLCOLOR_DEFAULT  = new Color(255, 51, 51);
	/**
	 * Default color for unhighlighting.
	 */
	public final Color UHLCOLOR_DEFAULT = Color.WHITE;
		
	/**
	 * Creates an Animator object with the specificed GPanel and the animation string
	 * to be executed on it. The DelayPeriod is set to a default value of 2000.
	 * The HighLightColor is set to a default value of HLCOLOR_DEFAULT, and the 
	 * UnHighLightColor is set to a default value of UHLCOLOR_DEFAULT.
	 * @param currGPanel Refers to the GPanel object on which animation is to done.
	 * @param currAS Refers to the animation string that has to be executed.
	 */
	public Animator(GPanel currGPanel, Vector<String> currAS)
	{
		super();
		this.currGPanel = currGPanel;
		this.currAS = currAS;
		this.HLColor = HLCOLOR_DEFAULT;
		this.UHLColor = UHLCOLOR_DEFAULT;
	}
	
	/**
	 * Sets DelayPeriod to a new value.  DelayPeriod specifies the delay in
	 * milliseconds between execution of each animation command.
	 * @param d The new DelayPeriod in milliseconds.
	 */
	public void setDelayPeriod(int d)
	{
		delayPeriod = d;
	}
	
	/**
	 * Returns the present DelayPeriod being used by the Animator object.
	 */
	public int getDelayPeriod()
	{
		return delayPeriod;
	}
	
	/**
	 * Sets new hightlight color.
	 */
	public void setHighLightColor(Color HLColor)
	{
		this.HLColor = HLColor;
	}
	
	/**
	 * Returns the current highhight color.
	 */
	public Color getHighLightColor()
	{
		return HLColor;
	}
	
	/**
	 * Sets new unhightlight color.
	 */	
	public void setUnHightLightColor(Color UHLColor)
	{
		this.UHLColor = UHLColor;	
	}
	
	/**
	 * Returns the current unhighhight color.
	 */
	public Color getUnHighLightColor()
	{
		return UHLColor;
	}
	
	/**
	 * Does all the interpretation (a crude interpreter) of the animation string
	 * and also sends appropriate messages to GPanel for animation to take effect.
	 * All invalid commands are neglected.
	 * run() should not be called directly.  It must be activated using start().
	 */
	public void run()
	{
		// index for animation string
		int asi = 0;
		// indicates that current command is prefixed with "N"
		boolean noDelayFlag;
		// specifies the index into the current command at which command starts
		// if N is prefixed commands starts from index 1 else starts from index 0
		int ci;
		
		try
		{
			while( currAS.get(asi).equals("END") == false)
			{
				// check if prefixed with N
				if(currAS.get(asi).charAt(0) == 'N')
				{
					noDelayFlag = true;
					ci = 1;
				}
				else
				{
					noDelayFlag = false;
					ci = 0;
				}
				
				if(currAS.get(asi).charAt(ci) == 'V')
				{
					int v = Integer.parseInt(currAS.get(asi).substring(ci+1));
					
					VertexAttr va = currGPanel.getGraph().getVertex(v);
					va.setFillColor(HLColor);
					currGPanel.getGraph().setVertex(v, va);
				}
				else if(currAS.get(asi).charAt(ci) == 'E')
				{
					int from = Integer.parseInt(currAS.get(asi).substring(ci+1, currAS.get(asi).indexOf(',')));
					int to = Integer.parseInt(currAS.get(asi).substring(currAS.get(asi).indexOf(',') + 1, currAS.get(asi).length()));
					
					EdgeAttr ea = currGPanel.getGraph().getEdge(from, to);
					ea.setColor(HLColor);
					ea.setWidth(2);
					currGPanel.getGraph().setEdge(from, to, ea);
				}
				if(currAS.get(asi).charAt(ci) == 'v')
				{
					int v = Integer.parseInt(currAS.get(asi).substring(ci+1));
					
					VertexAttr va = currGPanel.getGraph().getVertex(v);
					va.setFillColor(UHLColor);
					currGPanel.getGraph().setVertex(v, va);
				}
				else if(currAS.get(asi).charAt(ci) == 'e')
				{
					int from = Integer.parseInt(currAS.get(asi).substring(ci+1, currAS.get(asi).indexOf(',')));
					int to = Integer.parseInt(currAS.get(asi).substring(currAS.get(asi).indexOf(',') + 1, currAS.get(asi).length()));
					
					EdgeAttr ea = currGPanel.getGraph().getEdge(from, to);
					ea.setColor(UHLColor);
					ea.setWidth(1);
					currGPanel.getGraph().setEdge(from, to, ea);
				}
				else if(currAS.get(asi).charAt(ci) == 'R')
				{
					currGPanel.refresh();
					currGPanel.saveForRefresh();
				}
				
				asi++;
				
				currGPanel.repaint();
				
				if(noDelayFlag == false)
					sleep(delayPeriod);
			}
		}
		catch(InterruptedException e)
		{
			// Not a problem even if failed here.
		}
		catch(IllegalVertexException e)
		{
			System.out.println("Check error in Animator.run");
		}
	}
}